﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;
using System.Windows.Media;
using JumpJump.Runtime;
using System.Windows.Shapes;
using JumpJump.AI;
using System.Threading;
using System.Windows.Threading;

namespace JumpJump.UI
{
    public class ChessBoardControl:Canvas
    {
        #region Fields

        private ResourceDictionary resources = new ResourceDictionary();

        private List<PiecesControl> UserPieces = new List<PiecesControl>();

        private List<PiecesControl> PrickPieces = new List<PiecesControl>();

        private PiecesControl selectedPieceControl = null;

        private Piece readyToMovePiece = null;

        private bool _canUserMove = false;

        private Chessboard _chessboard;

        private List<DependencyObject> hitResultsList = new List<DependencyObject>();

        private List<PiecesControl> _canMoveToList = new List<PiecesControl>(20);

        private AIPrick _prick;

        private PiecesControl _prickLastMoveFrom;

        private PiecesControl _prickLastMoveTo;

        public TextBox messenger;

        #endregion

        #region Public Propertites


        #endregion

        #region Constructor

        public ChessBoardControl()
        {
            this.resources.Source = new Uri("pack://application:,,,/UIStyle.xaml", UriKind.Absolute);
            Initinal();
        }

        #endregion

        #region Public Methods

        public void Initinal()
        {
            this.ClearUp();
            for (int i = 0; i < 10; i++)
            {
                double dx = -1;
                double dy = -1;

                PiecesControl userPiece = InitinalUserPiece(i);
                Coordinate position = _chessboard.UserPieces[i].Position;
                Coordinate.TransformXToDx(position.X, position.Y, ref dx, ref dy);
                Canvas.SetLeft(userPiece, dx);
                Canvas.SetTop(userPiece, dy);
                UserPieces.Add(userPiece);
                this.Children.Add(userPiece);

                PiecesControl priciPiece = InitinalPrickPiece(i);
                position = _chessboard.PrickPieces[i].Position;
                Coordinate.TransformXToDx(position.X, position.Y, ref dx, ref dy);
                Canvas.SetLeft(priciPiece, dx);
                Canvas.SetTop(priciPiece, dy);
                PrickPieces.Add(priciPiece);
                this.Children.Add(priciPiece);
            }
            _chessboard.Replay = new Replay(DateTime.Now);
        }

        public void ClearUp()
        {
            this.Children.Clear();
            this._canMoveToList.Clear();
            this._canUserMove = false;
            this.PrickPieces.Clear();
            this.UserPieces.Clear();
            this.selectedPieceControl = null;
            this.readyToMovePiece = null;
            this._chessboard = new Chessboard();
            this._prick = new AIPrick(_chessboard);
            string datPath = System.IO.Directory.GetCurrentDirectory() + "\\chessmanual.dat";
            this._prick.LoadChessManual(datPath);
        }

        public void Save(bool showLastBoard)
        {
            this._chessboard.Replay.IsShowLastBoard = showLastBoard;
            if(showLastBoard)
            {
                List<Coordinate> userPiecesCoor=new List<Coordinate>();
                foreach (Piece p in _chessboard.UserPieces)
                {
                    userPiecesCoor.Add(p.Position);
                }

                List<Coordinate> prickPiecesCoor = new List<Coordinate>();
                foreach (Piece p in _chessboard.PrickPieces)
                {
                    prickPiecesCoor.Add(p.Position);
                }

                this._chessboard.Replay.SaveLastPiecesPosition(userPiecesCoor, prickPiecesCoor);
            }

            this._chessboard.Replay.Save();
        }

        public void Load(string fileName)
        {
            Replay loadedRep = Replay.Load(fileName);
            this._chessboard.Replay = loadedRep;
            if (loadedRep.IsShowLastBoard == true)
            {
                PlacePiecesInRep(loadedRep.UserPiecesCoordinate, loadedRep.PrickPiecesCoordinate);
            }
        }

        #endregion

        #region Private Methods

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            if (selectedPieceControl == null)
            {
                _canUserMove = false;
            }
            else
            {
                _canUserMove = true;
            }

            Point mouseLocation = e.GetPosition(this);

            int x = -1;
            int y = -1;
            Coordinate.TransformDxToX(mouseLocation.X, mouseLocation.Y, ref x, ref y);

            hitResultsList.Clear();
            VisualTreeHelper.HitTest(this, null,
                new HitTestResultCallback(MyHitTestResult),
                new PointHitTestParameters(mouseLocation));
            PiecesControl hitedPiece = null;
            foreach (DependencyObject obj in hitResultsList)
            {
                if (obj is System.Windows.Shapes.Ellipse)
                {
                    System.Windows.Shapes.Ellipse ellipse = obj as Ellipse;
                    Grid grid = ellipse.Parent as Grid;
                    hitedPiece = grid.TemplatedParent as PiecesControl;

                    //if hit a visual piece, set it null
                    if (hitedPiece.IsVisualPiece == true || hitedPiece.IsLastMove == true)
                    {
                        hitedPiece = null;
                    }
                }
            }

            //hit a piece
            if (hitedPiece != null)
            {
                // if select piece is not enable, retrun
                if (hitedPiece.IsEnabled == false)
                {
                    return;
                }
                //if no selected piece
                if (selectedPieceControl == null)
                {
                    //if hitpiece is user piece, mark hitpiece as selected piece
                    //user can move when next hit
                    if (hitedPiece.PlayerID == Chessboard.UserID)
                    {
                        selectedPieceControl = hitedPiece;
                        _canUserMove = true;
                    }
                    //if hitpiece is AI piece, do nothing and return
                    else
                    {
                        _canUserMove = false;
                    }
                }
                //if user already have a selected piece
                else
                {
                    //if hitpiece is the user piece
                    if (hitedPiece.PlayerID == Chessboard.UserID)
                    {
                        //if hit the same piece again
                        //cancel the selected piece
                        if (hitedPiece.PieceID == selectedPieceControl.PieceID)
                        {
                            hitedPiece.IsChecked = false;
                            selectedPieceControl = null;
                            _canUserMove = false;
                        }
                        //if hit another piece, refresh the selected piece
                        else
                        {
                            selectedPieceControl = hitedPiece;
                            _canUserMove = true;
                        }
                    }
                    //if hitpiece is a AI piece
                    //do nothing, user need another hit
                    else
                    {
                        _canUserMove = true;
                    }
                }

                if (_canUserMove == true)
                {
                    RemoveCanMovePositionsFromBoard(this._canMoveToList);
                    readyToMovePiece = _chessboard.UserPieces[selectedPieceControl.PieceID];
                    readyToMovePiece.GetNeighbours();
                    readyToMovePiece.GetNextPositions(_chessboard.Board);
                    RefreshTheCanMovePosition(readyToMovePiece.CanMoveToPositionList.ToList());
                }
                else
                {
                    RemoveCanMovePositionsFromBoard(this._canMoveToList);
                }

                return;
            }
            //hit an empty place
            else
            {
                //if user do not have a selected piece
                //this hit will do nothing
                if (selectedPieceControl == null)
                {
                    _canUserMove = false;
                    return;
                }
                //if user have a selected piece
                else
                {
                    //if the hit place is not in chessboard
                    //do nothing
                    if (!Validator.ValidatePosition(x, y))
                    {
                        _canUserMove = true;
                        return;
                    }
                    //if hit place is not empty
                    //do nothing
                    else if (!readyToMovePiece.CanMoveToPositionList.Contains(new Coordinate(x, y)))
                    {
                        _canUserMove = true;
                        return;
                    }
                    //double check the canmove 
                    // if can move, move the selected piece
                    else if (_canUserMove)
                    {
                        PieceMove(selectedPieceControl, new Coordinate(x, y));
                        if (IsAnyoneWin(Chessboard.UserID))
                        {
                            return;
                        }
                        RemoveCanMovePositionsFromBoard(this._canMoveToList);
                        selectedPieceControl.IsChecked = false;
                        selectedPieceControl = null;
                        _canUserMove = false;
                        PrickMove();
                        IsAnyoneWin(Chessboard.PrickID);
                        if (IsAnyoneWin(Chessboard.UserID))
                        {
                            return;
                        }
                    }
                }
            }
        }

        private bool IsAnyoneWin(int playerID)
        {
            string winnerName = playerID == Chessboard.UserID ? "You" : "AI";
            int winner = _chessboard.WinnerCheck();
            if (winner == playerID)
            {
                MessageBoxResult result = MessageBox.Show(winnerName + " WIN the game!! Try another round?",
                    "Game Over", MessageBoxButton.YesNo);
                if (result == MessageBoxResult.Yes)
                {
                    this.Initinal();
                }
                Save(false);
                return true;
            }
            else
            {
                return false;
            }
        }

        private void PieceMove(PiecesControl selectedPiece, Coordinate target)
        {
            Piece pieceToMove = _chessboard.UserPieces[selectedPiece.PieceID];
            PieceMoveAnimation(selectedPiece, pieceToMove, target);
            _chessboard.PieceMove(pieceToMove, target);
        }

        private bool IsSamePosition(Coordinate left, Coordinate right)
        {
            return Coordinate.Equals(right, left);
        }

        private PiecesControl InitinalUserPiece(int i)
        {
            PiecesControl userPiece = new PiecesControl();
            userPiece.Name = "UserPiece_" + i;
            userPiece.GroupName = "UserPieces";
            userPiece.PlayerID = 1;
            userPiece.PieceID = i;
            userPiece.IsVisualPiece = false;
            userPiece.IsLastMove = false;
            userPiece.IsEnabled = true;
            userPiece.Style = (Style)resources["PieceStyle"];
            return userPiece;
        }

        private PiecesControl InitinalPrickPiece(int i)
        {
            PiecesControl prickPiece = new PiecesControl();
            prickPiece.Name = "PrickPiece_" + i;
            prickPiece.GroupName = "PrickPieces";
            prickPiece.PlayerID = 2;
            prickPiece.PieceID = i;
            prickPiece.IsVisualPiece = false;
            prickPiece.IsLastMove = false;
            prickPiece.IsEnabled = false;
            prickPiece.Style = (Style)resources["PieceStyle"];
            return prickPiece;
        }

        private void RefreshTheCanMovePosition(List<Coordinate> validPositionList)
        {
            foreach (Coordinate oneTarget in validPositionList)
            {
                PiecesControl visualPiece = new PiecesControl();
                visualPiece.IsVisualPiece = true;
                visualPiece.Style = (Style)resources["PieceStyle"];
                double dx = 0;
                double dy = 0;
                Coordinate.TransformXToDx(oneTarget.X, oneTarget.Y, ref dx, ref dy);
                _canMoveToList.Add(visualPiece);
                this.Children.Add(visualPiece);
                Canvas.SetLeft(visualPiece, dx);
                Canvas.SetTop(visualPiece, dy);
            }
        }

        private void RemoveCanMovePositionsFromBoard(List<PiecesControl> targetList)
        {
            foreach (PiecesControl oneTarget in targetList)
            {
                this.Children.Remove(oneTarget);
                oneTarget.IsVisualPiece = false;
            }
            targetList.Clear();
        }

        private void PrickMove()
        {
            if (_prickLastMoveTo != null)
            {
                _prickLastMoveTo.IsLastMove = false;
            }
            Movement moveResult = _prick.MoveOnePiece();
            PiecesControl pieceControlToMove = this.PrickPieces[moveResult.PieceID];
            _prickLastMoveTo = pieceControlToMove;
            _prickLastMoveTo.IsLastMove = true;
            PieceMoveAnimation(pieceControlToMove, _chessboard.PrickPieces[moveResult.PieceID], moveResult.Target);

            _chessboard.PieceMove(_chessboard.PrickPieces[moveResult.PieceID], moveResult.Target);
            RefreshPrickLastMove(moveResult);
            Recording(moveResult);
        }

        private HitTestResultBehavior MyHitTestResult(HitTestResult result)
        {
            // Add the hit test result to the list that will be processed after the enumeration.
            hitResultsList.Add(result.VisualHit);

            // Set the behavior to return visuals at all z-order levels.
            return HitTestResultBehavior.Continue;
        }

        private void Recording(Movement move)
        {
            StringBuilder message = new StringBuilder();
            message.Append("No." + move.PieceID + " Piece Moved\n");
            message.Append("From: (" + move.From.X + ", " + move.From.Y + ")\n");
            message.Append("To: (" + move.Target.X + ", " + move.Target.Y + ")\n");
            message.Append("Source: " + move.Source + "\n");
            message.Append("Choose this move from " + _prick.AIEngine.AllMovementCount + " valid Movement\n");
            message.Append("Movement Score: " + move.Score);

            this.messenger.Text = message.ToString();
        }

        private void RefreshPrickLastMove(Movement move)
        {
            this.Children.Remove(_prickLastMoveFrom);

            _prickLastMoveFrom = new PiecesControl();
            _prickLastMoveFrom.IsLastMove = true;
            _prickLastMoveFrom.Style = (Style)resources["PieceStyle"];
            double dx = 0;
            double dy = 0;
            Coordinate.TransformXToDx(move.From.X, move.From.Y, ref dx, ref dy);
            this.Children.Add(_prickLastMoveFrom);
            Canvas.SetLeft(_prickLastMoveFrom, dx);
            Canvas.SetTop(_prickLastMoveFrom, dy);
        }

        private void PieceMoveAnimation(PiecesControl selectedPieceControl, Piece piece, Coordinate target)
        {
            piece.GetNextPositions(_chessboard.Board);
            List<Coordinate> path = piece.GetOneShortestPath(_chessboard, target);
            RemoveCanMovePositionsFromBoard(this._canMoveToList);
            foreach (Coordinate coor in path)
            {
                double targetDx = -1;
                double targetDy = -1;
                Coordinate.TransformXToDx(coor.X, coor.Y, ref targetDx, ref targetDy);
                Canvas.SetLeft(selectedPieceControl, targetDx);
                Canvas.SetTop(selectedPieceControl, targetDy);
                Thread.Sleep(300);
                DoEvents();
            }
        }

        private static DispatcherOperationCallback exitFrameCallback = new DispatcherOperationCallback(ExitFrame);

        public static void DoEvents()
        {
            DispatcherFrame nestedFrame = new DispatcherFrame();

            DispatcherOperation exitOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, exitFrameCallback, nestedFrame);

            Dispatcher.PushFrame(nestedFrame);

            if (exitOperation.Status != DispatcherOperationStatus.Completed)
            {
                exitOperation.Abort();
            }
        }

        private static Object ExitFrame(Object state)
        {
            DispatcherFrame frame = state as DispatcherFrame;
            frame.Continue = false;
            return null;
        }
        
        private void PlacePiecesInRep(List<Coordinate> userPieces, List<Coordinate> prickPieces)
        {
            this.ClearUp();
            for (int i = 0; i < 10; i++)
            {
                double dx = -1;
                double dy = -1;

                PiecesControl userPiece = InitinalUserPiece(i);
                Coordinate position = userPieces[i];
                Coordinate.TransformXToDx(position.X, position.Y, ref dx, ref dy);
                Canvas.SetLeft(userPiece, dx);
                Canvas.SetTop(userPiece, dy);
                UserPieces.Add(userPiece);
                this.Children.Add(userPiece);
                _chessboard.PieceMoveWithoutInsertToRep(_chessboard.UserPieces[i], position);

                PiecesControl prickPiece = InitinalPrickPiece(i);
                position = prickPieces[i];
                Coordinate.TransformXToDx(position.X, position.Y, ref dx, ref dy);
                Canvas.SetLeft(prickPiece, dx);
                Canvas.SetTop(prickPiece, dy);
                PrickPieces.Add(prickPiece);
                this.Children.Add(prickPiece);
                _chessboard.PieceMoveWithoutInsertToRep(_chessboard.PrickPieces[i], position);
            }
        }
        #endregion 
    }
}
